/*
 * This file is part of Viper.  Viper is free software: you can
 * redistribute it and/or modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation, version 2.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Copyright GruntXProductions
 */

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.IO;
using System.Text;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace Viper
{
    public partial class Assembly
    {
		private void reAdjustSymbols(uint start, int align)
		{
			foreach(Symbol s in this.SymbolTable)
			{
				if(s.Address <= this.position)
				{
					if(s.Address >= start)
						s.Address = (uint)((int)s.Address + align);
				}
			}
		}
		public void Optimize()
		{
			
			List<Instruction> newIL = new List<Instruction>();
			
			int i = 0;
			Instruction op1 = new Instruction();
			Instruction op2 = new Instruction();
		
			uint addr = 0;
			for(i = 0; i < innerCode.Count; i++)
			{
				if(i >= 2)
				{
					op2 = newIL[newIL.Count - 2];
					op1 = newIL[newIL.Count - 1];
				}
				if(innerCode[i] is Opcodes.Add || innerCode[i] is Opcodes.sub || innerCode[i] is Opcodes.mul || innerCode[i] is Opcodes.div)
				{
					if(op1 is Opcodes.push_d && op2 is Opcodes.push_d )
					{
						uint val1 = BitConverter.ToUInt32(op1.Bytecode, 2);
						uint val2 = BitConverter.ToUInt32(op2.Bytecode, 2);
						uint total = 0;
						if(innerCode[i] is Opcodes.Add)
							total = val1 + val2;
						else if(innerCode[i] is Opcodes.sub)
							total = val2 - val1;
						else if(innerCode[i] is Opcodes.mul)
							total = val2 * val1;
						else if(innerCode[i] is Opcodes.div)
							total = val2 / val1;
						newIL.RemoveAt((newIL.Count - 2));
						newIL[newIL.Count - 1].Address = addr - 6;
						for(int b = 0; b < 4; b++)
							newIL[newIL.Count - 1].Bytecode[2 + b] = BitConverter.GetBytes(total)[b];				
						addr -= 6;
						reAdjustSymbols(addr, -8);
						continue;
					}
					
				}
				else if (innerCode[i] is Opcodes.convb_d)
				{
					if(op1 is Opcodes.push_b)
					{
						addr -= (uint)op1.Bytecode.Length;
						uint val1 = (uint)op1.Bytecode[2];
						Console.WriteLine(val1.ToString());
						Opcodes.push_d npd = new Opcodes.push_d(val1);
						npd.Address = op1.Address;
						newIL[newIL.Count - 1] = npd;
						reAdjustSymbols(addr, 1);
						addr += (uint)npd.Bytecode.Length;
						continue;
					}
					
				}
				innerCode[i].Address = addr;
				newIL.Add(innerCode[i]);
				addr += (uint)innerCode[i].Bytecode.Length;
			
			}
			uint taddr = 0;
			Console.Write("" + this.position.ToString() + " VS " + addr.ToString());
			this.position = addr;
			this.innerCode = newIL;
		}
	}
	
}